#ifndef OPS_H
#define OPS_H

namespace binary_assign_ops
{
	#define DECLARE_BINARY_ASSIGN_OPS( NAME, OP )		\
	struct NAME						\
	{							\
		template <class T1, class T2>	\
		static inline void apply( T1&& v1, T2&& v2 )	\
			{ v1 OP v2; }			\
	};							\
	
	DECLARE_BINARY_ASSIGN_OPS( equal, = );
	DECLARE_BINARY_ASSIGN_OPS( plus_equal, += );
	DECLARE_BINARY_ASSIGN_OPS( minus_equal, -= );
	DECLARE_BINARY_ASSIGN_OPS( multiply_equal, *= );
	DECLARE_BINARY_ASSIGN_OPS( divide_equal, /= );
	DECLARE_BINARY_ASSIGN_OPS( mod_equal, %= );
	DECLARE_BINARY_ASSIGN_OPS( xor_equal, ^= );
	DECLARE_BINARY_ASSIGN_OPS( bitand_equal, &= );
	DECLARE_BINARY_ASSIGN_OPS( bitor_equal, |= );
	DECLARE_BINARY_ASSIGN_OPS( right_shift_equal, >>= );
	DECLARE_BINARY_ASSIGN_OPS( left_shift_equal, <<= );
};

namespace binary_ops
{
	#define DECLARE_BINARY_OPS( NAME, OP )		\
	struct NAME\
	{ \
		template <class T1, class T2> \
		inline static constexpr auto apply( T1&& v1, T2&& v2 ) -> decltype( v1 OP v2 ) \
		{ return std::move( v1 OP v2 ); } \
	};
	
	DECLARE_BINARY_OPS( plus_op, + );
	DECLARE_BINARY_OPS( minus_op, - );
	DECLARE_BINARY_OPS( multiply_op, * );
	DECLARE_BINARY_OPS( divide_op, / );
	DECLARE_BINARY_OPS( mod_op, % );
	DECLARE_BINARY_OPS( xor_op, ^ );
	DECLARE_BINARY_OPS( bitand_op, & );
	DECLARE_BINARY_OPS( bitor_op, | );
	DECLARE_BINARY_OPS( right_shift_op, >> );
	DECLARE_BINARY_OPS( left_shift_op, << );
	
	DECLARE_BINARY_OPS( greater_op, > )
	DECLARE_BINARY_OPS( less_op, < )
	DECLARE_BINARY_OPS( greater_or_equal_op, >= )
	DECLARE_BINARY_OPS( less_or_equal_op, <= )
	DECLARE_BINARY_OPS( equal_op, == )
	DECLARE_BINARY_OPS( not_equal_op, != )
	DECLARE_BINARY_OPS( logical_and_op, && )
	DECLARE_BINARY_OPS( logical_or_op, || )
};

namespace unary_ops
{
	#define DECLARE_UNARY_OPS( NAME, OP )		\
	struct NAME\
	{ \
		template <class T> \
		inline static constexpr auto apply( T&& v ) -> decltype( OP v ) \
		{ return std::move( OP v ); } \
	};
	
	DECLARE_UNARY_OPS( minus_op, - );
	DECLARE_UNARY_OPS( plus_op, + );
	DECLARE_UNARY_OPS( bitwise_not_op, ~ );
	DECLARE_UNARY_OPS( logical_not_op, ! );
}

#endif //!< OPS_H